NexusPi Git Node
MICRORETICULUM_BUGS.md 6aee89d5a2c5aa4c2f1d8bc820facfdba0e28e4d (6aee89d5) Text, 18.63 KB
microReticulum Bug Report
1. Copy-vs-Reference Bugs in Transport.cpp
Summary
Several locations in T383838Transport.cpp retrieve T383838LinkEntry or T383838DestinationEntry values from T383838std::map containers by copy instead of by reference. Subsequent mutations (setting T383838_validated, updating T383838_timestamp) only affect the local copy — the actual map entry is never updated.
This is a Python→C++ porting issue: in Python, T383838dict[key] returns a reference to the value, so T383838transport.link_table[link_id][0] = time.time() mutates the dict entry in place. In C++, T383838(*iter).second returns a copy when assigned to a non-reference variable.
Bug 1a — LRPROOF handler: T383838_validated never set on map entry (CRITICAL)
File: T383838Transport.cpp, LRPROOF handling section
Code (before fix):
T282828
Te6edf3LinkEntry Te6edf3link_entry Tff7b72= Tb4b4b4(Tff7b72*Te6edf3_link_tableTb4b4b4.Te6edf3findTb4b4b4(Te6edf3packetTb4b4b4.Te6edf3destination_hashTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4)Tb4b4b4.Te6edf3secondTb4b4b4; T8b949e// COPY
T8b949e// ... later:
Te6edf3link_entryTb4b4b4.Te6edf3_validated Tff7b72= Tffa657trueTb4b4b4; T8b949e// Only updates local copy
Impact: The link_table entry's T383838_validated stays T383838false. The culling code in T383838jobs() checks:
T282828
Tff7b72if Tb4b4b4(Te6edf3link_entryTb4b4b4.Te6edf3_validatedTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3OSTff7b72:Tff7b72:Te6edf3timeTb4b4b4(Tb4b4b4) Tff7b72> Tb4b4b4(Te6edf3link_entryTb4b4b4.Te6edf3_timestamp Tff7b72+ Te6edf3LINK_TIMEOUTTb4b4b4)Tb4b4b4) Tb4b4b4{ Tb4b4b4.Tb4b4b4.Tb4b4b4. Tb4b4b4} T8b949e// 15 minutes
Tb4b4b4} Tff7b72else Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3OSTff7b72:Tff7b72:Te6edf3timeTb4b4b4(Tb4b4b4) Tff7b72> Te6edf3link_entryTb4b4b4.Te6edf3_proof_timeoutTb4b4b4) Tb4b4b4{ Tb4b4b4.Tb4b4b4.Tb4b4b4. Tb4b4b4} T8b949e// 6-18 seconds
Tb4b4b4}
Since T383838_validated is never set to T383838true, the entry is culled at T383838proof_timeout (6 × hops = 6–18 seconds) instead of T383838LINK_TIMEOUT (15 minutes). All subsequent link data packets (resource chunks, keepalives, etc.) are silently dropped once the entry is removed.
Symptom: Resource transfers start successfully but fail after a few seconds. Link establishes, some data transfers, then all traffic stops. Affects both directions.
Fix: Change to reference:
T282828
Te6edf3LinkEntryTff7b72& Te6edf3link_entry Tff7b72= Tb4b4b4(Tff7b72*Te6edf3_link_tableTb4b4b4.Te6edf3findTb4b4b4(Te6edf3packetTb4b4b4.Te6edf3destination_hashTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4)Tb4b4b4.Te6edf3secondTb4b4b4;
Bug 1b — Link transport handler: T383838_timestamp never refreshed (CRITICAL)
File: T383838Transport.cpp, link transport handling section
Code (before fix):
T282828
Te6edf3LinkEntry Te6edf3link_entry Tff7b72= Tb4b4b4(Tff7b72*Te6edf3link_iterTb4b4b4)Tb4b4b4.Te6edf3secondTb4b4b4; T8b949e// COPY
T8b949e// ... later:
Te6edf3link_entryTb4b4b4.Te6edf3_timestamp Tff7b72= Te6edf3OSTff7b72:Tff7b72:Te6edf3timeTb4b4b4(Tb4b4b4)Tb4b4b4; T8b949e// Only updates local copy
Impact: Even with Bug 1a fixed, the linktable entry's T383838_timestamp is set once (at LINKREQUEST time) and never refreshed during ongoing link data forwarding. For long-running transfers exceeding T383838LINK_TIMEOUT (KEEPALIVE=360s, STALETIME=720s, LINK_TIMEOUT=900s = 15 minutes), the entry is eventually culled and the transfer fails.
Fix: Change to reference:
T282828
Te6edf3LinkEntryTff7b72& Te6edf3link_entry Tff7b72= Tb4b4b4(Tff7b72*Te6edf3link_iterTb4b4b4)Tb4b4b4.Te6edf3secondTb4b4b4;
Bug 1c — Standard inbound transport forwarding: T383838_timestamp never refreshed
File: T383838Transport.cpp, inbound transport forwarding (HEADER_2 packets where we are the designated next-hop)
Code (before fix):
T282828
Te6edf3DestinationEntry Te6edf3destination_entry Tff7b72= Tb4b4b4(Tff7b72*Te6edf3destination_iterTb4b4b4)Tb4b4b4.Te6edf3secondTb4b4b4; T8b949e// COPY
T8b949e// ... later:
Te6edf3destination_entryTb4b4b4.Te6edf3_timestamp Tff7b72= Te6edf3OSTff7b72:Tff7b72:Te6edf3timeTb4b4b4(Tb4b4b4)Tb4b4b4; T8b949e// Only updates local copy
Impact: Path timestamps are never refreshed when packets are actively being forwarded along that path. Paths could be culled while still in active use, though the timeout is typically long enough (DESTINATIONTIMEOUT) that this is less likely to cause issues than the linktable bugs.
Fix: Change to reference:
T282828
Te6edf3DestinationEntryTff7b72& Te6edf3destination_entry Tff7b72= Tb4b4b4(Tff7b72*Te6edf3destination_iterTb4b4b4)Tb4b4b4.Te6edf3secondTb4b4b4;
Bug 1d — Outbound transport forwarding: T383838_timestamp never refreshed
File: T383838Transport.cpp, T383838outbound() method
Code (before fix):
T282828
Te6edf3DestinationEntry Te6edf3destination_entry Tff7b72= Tb4b4b4(Tff7b72*Te6edf3_destination_tableTb4b4b4.Te6edf3findTb4b4b4(Te6edf3packetTb4b4b4.Te6edf3destination_hashTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4)Tb4b4b4.Te6edf3secondTb4b4b4; T8b949e// COPY
T8b949e// ... later:
Te6edf3destination_entryTb4b4b4.Te6edf3_timestamp Tff7b72= Te6edf3OSTff7b72:Tff7b72:Te6edf3timeTb4b4b4(Tb4b4b4)Tb4b4b4; T8b949e// Only updates local copy
Impact: Same as 1c — outbound path forwarding never refreshes the path timestamp.
Fix: Change to reference:
T282828
Te6edf3DestinationEntryTff7b72& Te6edf3destination_entry Tff7b72= Tb4b4b4(Tff7b72*Te6edf3_destination_tableTb4b4b4.Te6edf3findTb4b4b4(Te6edf3packetTb4b4b4.Te6edf3destination_hashTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4)Tb4b4b4.Te6edf3secondTb4b4b4;
How to audit for more
Search for the pattern: assignments from map iterators without T383838&:
T282828
grep -n "Entry [a-z_]* = " Transport.cpp
Any line matching T383838SomeEntry variable_name = (*iter).second; (without T383838& after the type) that later mutates a field of T383838variable_name is a bug.
2. Potential additional copy-vs-reference instances (non-mutating — safe but wasteful)
The following locations also copy entries but only read from them (no mutation). They are not bugs but are unnecessarily copying large structs:
• T383838for_local_client check: T383838LinkEntry link_entry = (*link_iter).second; — read-only, safe
• T383838for_local_client check: T383838DestinationEntry destination_entry = (*destination_iter).second; — read-only, safe
• T383838proof_for_local_client check: T383838ReverseEntry reverse_entry = (*reverse_iter).second; — read-only, safe
• Several T383838DestinationEntry copies in T383838has_path(), T383838hops_to(), T383838next_hop(), T383838next_hop_interface() — read-only, safe
These could be changed to T383838const auto& for efficiency but are not correctness bugs.
3. T383838std::map::insert() silently fails on existing keys
Summary
In Python, T383838dict[key] = value always overwrites. In C++, T383838std::map::insert({key, value}) is a no-op if the key already exists — it silently discards the new value and returns the old entry.
Bug 3a — T383838_destination_table path updates silently ignored
File: T383838Transport.cpp, path table update code
Code (before fix):
T282828
Te6edf3_destination_tableTb4b4b4.Te6edf3insertTb4b4b4(Tb4b4b4{Te6edf3destination_hashTb4b4b4, Te6edf3new_destination_entryTb4b4b4}Tb4b4b4)Tb4b4b4;
T8b949e// If destination_hash already exists, the insert does NOTHING.
T8b949e// The old (stale) path entry remains.
Impact: When a destination announces a new path (e.g. it roamed to a different transport node), the path table keeps the old stale entry. Packets continue to be routed to the old path, which may no longer work.
Fix: Erase before insert:
T282828
Te6edf3_destination_tableTb4b4b4.Te6edf3eraseTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4;
Te6edf3_destination_tableTb4b4b4.Te6edf3insertTb4b4b4(Tb4b4b4{Te6edf3destination_hashTb4b4b4, Te6edf3new_destination_entryTb4b4b4}Tb4b4b4)Tb4b4b4;
Note: This same pattern (T383838insert without T383838erase) should be audited across ALL map insertions in the codebase. Any map that might receive updated entries for existing keys needs the erase-before-insert pattern, or should use T383838operator[] or T383838insert_or_assign().
4. Memory Leaks — Unbounded Data Structures
Bug 4a — T383838_boundary_local_addresses: no cap, no eviction (HIGH RISK)
File: T383838Transport.cpp
The T383838_boundary_local_addresses set accumulates every local device address seen via LoRa announces. There is no size cap and no eviction mechanism. On a long-running boundary node that sees many transient devices, this grows without bound.
Impact: Slow heap exhaustion over days/weeks of operation. Particularly problematic on ESP32 with limited RAM.
Fix needed: Add a size cap (e.g. 200) with timestamp-based or LRU eviction, similar to how T383838_boundary_mentioned_addresses is capped.
Bug 4b — T383838_held_announces: no cap, can orphan
File: T383838Transport.cpp
The T383838_held_announces map stores announces waiting to be retransmitted. There is no size cap. If the retransmit never triggers (e.g. the outbound interface disappears), entries can be orphaned and accumulate indefinitely.
Impact: Slow memory leak, exacerbated on busy networks with many announces.
Fix needed: Add a size cap or timeout-based eviction.
Bug 4c — T383838_pending_local_path_requests: entries never erased
File: T383838Transport.cpp
Status: Fixed (added T383838.erase(iter) call)
Entries in T383838_pending_local_path_requests were inserted but never removed after the path request was fulfilled. Over time the map grew without bound.
Bug 4d — T383838_path_requests: entries never culled
File: T383838Transport.cpp
Status: Fixed (added T383838DESTINATION_TIMEOUT-based culling in T383838jobs())
The T383838_path_requests map recorded timestamps of path requests but entries were never removed. Each unique destination hash that triggered a path request stayed in the map forever.
5. Spurious Path Request Broadcasts
Bug 5a — Boundary Path A sends PATH REQUEST for every link data packet
File: T383838Transport.cpp, boundary mode local→backbone forwarding
Code (before fix):
T282828
T8b949e// Boundary Path A: local device packet, no path in _destination_table
Tff7b72else Tb4b4b4{
Te6edf3DEBUGTb4b4b4(Ta5d6ff"Ta5d6ffBOUNDARY: No path to Ta5d6ff" Tff7b72+ Te6edf3packetTb4b4b4.Te6edf3destination_hashTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3toHexTb4b4b4(Tb4b4b4) Tff7b72+ Ta5d6ff"Ta5d6ff for local packet. Requesting path.Ta5d6ff"Tb4b4b4)Tb4b4b4;
Te6edf3request_pathTb4b4b4(Te6edf3packetTb4b4b4.Te6edf3destination_hashTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Impact: Link data packets are addressed to a T383838link_id (the link's unique identifier), not a destination hash. The T383838link_id will never be found in T383838_destination_table — it's only in T383838_link_table. So every link data packet from a local device triggers a T383838request_path(link_id) call, which broadcasts a PATH REQUEST for a hash that is not any destination.
For a resource transfer with 100 chunks, this sends 100 useless PATH REQUEST broadcasts over LoRa (with no deduplication — T383838request_path() always sends). This wastes radio airtime and can cause congestion-related timeouts on slow LoRa links.
Fix: Check if the destination is a known link_id before requesting a path:
T282828
Tff7b72else Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3_link_tableTb4b4b4.Te6edf3findTb4b4b4(Te6edf3packetTb4b4b4.Te6edf3destination_hashTb4b4b4(Tb4b4b4)Tb4b4b4) Tff7b72=Tff7b72= Te6edf3_link_tableTb4b4b4.Te6edf3endTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3DEBUGTb4b4b4(Ta5d6ff"Ta5d6ffBOUNDARY: No path to Ta5d6ff" Tff7b72+ Te6edf3packetTb4b4b4.Te6edf3destination_hashTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3toHexTb4b4b4(Tb4b4b4) Tff7b72+ Ta5d6ff"Ta5d6ff for local packet. Requesting path.Ta5d6ff"Tb4b4b4)Tb4b4b4;
Te6edf3request_pathTb4b4b4(Te6edf3packetTb4b4b4.Te6edf3destination_hashTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}
Bug 5b — Same issue in standard transport HEADER_2 fallback path
File: T383838Transport.cpp, inbound transport forwarding (where we are designated next-hop but destination_table lookup fails)
Same pattern: for link data packets with HEADER2/TRANSPORT headers where the transportid matches us, if the destinationhash (which is a linkid) isn't in the destination table, the code requests a path for the link_id — another spurious broadcast.
Fix: Same guard — check T383838_link_table before calling T383838request_path().
6. General Audit Recommendations
6a — Systematic T383838insert() audit
Every T383838std::map::insert() call in the codebase should be reviewed. The ones that are intentional "insert-if-not-exists" semantics are fine. The ones ported from Python T383838dict[key] = value (which overwrites) need to use erase+insert or T383838insert_or_assign().
6b — Systematic copy-vs-reference audit
Run:
T282828
grep -n Ta5d6ff"Entry [a-z_]* = .*\.second" Transport.cpp
Any match where the variable is later mutated (assigned to T383838._timestamp, T383838._validated, etc.) is a bug.
6c — Data structure caps
Every T383838std::map and T383838std::set in Transport that accumulates entries over time needs:
1. A size cap appropriate for ESP32 memory constraints
2. An eviction strategy (timestamp-based, LRU, or lexicographic)
3. Culling in the T383838jobs() periodic task
7. Packet Hashlist Timing Bug — Link Transport Breakage on Shared Media
Bug 7a — Premature packet hash insertion breaks link transport (FIXED)
File: T383838Transport.cpp, inbound() after packet_filter
Bug: The C++ code unconditionally added every accepted packet's hash to
T383838_packet_hashlist immediately (line ~1505), before link transport or proof
handling ran:
T282828
Te6edf3_packet_hashlistTb4b4b4.Te6edf3insertTb4b4b4(Te6edf3packetTb4b4b4.Te6edf3packet_hashTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4; T8b949e// Always, immediately
The Python reference implementation (T383838Transport.py lines 1362-1373)
defers insertion for two cases:
1. Packets whose T383838destination_hash is in T383838link_table (link data packets)
2. LRPROOF packets (type=PROOF, context=LRPROOF)
For link data: the hash is added later, inside the link transport
forwarding block, only after a valid outbound direction is confirmed
(T383838Transport.py line 1544).
For LRPROOF: the hash is never added (allowing duplicate proofs to be
processed on multiple interfaces).
Impact: On shared-medium interfaces (e.g. LoRa), a packet belonging to a
link that transports through this node may arrive on the "wrong" interface
first (e.g. received on LoRa before it arrives via TCP backbone). The
premature hash insertion causes the correct arrival to be filtered as a
duplicate for non-resource contexts (DATA ctx=0, LINKIDENTIFY, LRRTT,
LINKCLOSE). Resource contexts (RESOURCE, RESOURCEREQ, RESOURCEPRF)
are unaffected because they bypass the hashlist check in T383838packet_filter.
Fix: Defer hash insertion for link-table and LRPROOF packets, matching
the Python reference implementation. Add T383838_packet_hashlist.insert() inside
the link transport forwarding block after direction is confirmed.
8. Missing Link MTU Clamping — 70% Resource Transfer Stall (CRITICAL)
Summary
Status: FIXED (v1.0.12, 2026-02-28)
T383838Transport.cpp did not clamp the link MTU when forwarding T383838LINKREQUEST packets through the transport node. The Python reference implementation (T383838Transport.py lines 1458–1480) performs this clamping to ensure the negotiated link MTU does not exceed the capacity of any intermediate hop's interface.
Bug 8a — LINKREQUEST forwarded without MTU clamping
File: T383838Transport.cpp, all three LINKREQUEST forwarding paths:
1. Standard transport forwarding (next-hop routing, ~line 1729)
2. Boundary mode: local → backbone (~line 1875)
3. Boundary mode: backbone → local (~line 1959)
Code (before fix):
T282828
Tff7b72if Tb4b4b4(Te6edf3packetTb4b4b4.Te6edf3packet_typeTb4b4b4(Tb4b4b4) Tff7b72=Tff7b72= Te6edf3TypeTff7b72:Tff7b72:Te6edf3PacketTff7b72:Tff7b72:Te6edf3LINKREQUESTTb4b4b4) Tb4b4b4{
T8b949e// ... creates link_entry, inserts into _link_table ...
T8b949e// MTU signalling bytes in new_raw are forwarded UNCHANGED
Tb4b4b4}
The Python reference (T383838Transport.py lines 1458–1480) does:
T282828
Te6edf3path_mtu Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffmtu_from_lr_packetTb4b4b4(Te6edf3packetTb4b4b4)
Tff7b72if Te6edf3path_mtuTb4b4b4:
Te6edf3nh_mtu Tff7b72= Te6edf3outbound_interfaceTff7b72.Td2a8ffHW_MTU
Te6edf3ph_mtu Tff7b72= Te6edf3interfaceTff7b72.Td2a8ffHW_MTU Tff7b72if Te6edf3interface Tff7b72else Tff7b72None
Tff7b72if Te6edf3nh_mtu Tff7b72< Te6edf3path_mtu Tff7b72or Tb4b4b4(Te6edf3ph_mtu Tff7b72and Te6edf3ph_mtu Tff7b72< Te6edf3path_mtuTb4b4b4)Tb4b4b4:
Te6edf3path_mtu Tff7b72= Tffa657minTb4b4b4(Te6edf3nh_mtuTb4b4b4, Te6edf3ph_mtuTb4b4b4)
Te6edf3clamped_mtu Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffsignalling_bytesTb4b4b4(Te6edf3path_mtuTb4b4b4, Te6edf3modeTb4b4b4)
Te6edf3new_raw Tff7b72= Te6edf3new_rawTb4b4b4[Tb4b4b4:Tff7b72-Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffLINK_MTU_SIZETb4b4b4] Tff7b72+ Te6edf3clamped_mtu
Impact: When both endpoints connect via TCP (HWMTU=8192) through a V3 boundary node (HWMTU=1064):
1. Sender's T383838LINKREQUEST signals 8192-byte link MTU.
2. V3 forwards the request unchanged to the receiver.
3. Receiver confirms 8192-byte MTU → resource segments sized at ~7500 bytes (6 parts for a 46 KB file).
4. V3's HDLC deframer buffer (T383838rxbuf[1064]) silently truncates oversized segments.
5. Only 4 of 6 truncated segments partially survive → receiver times out waiting for remaining parts → permanent stall at ~70%.
Symptom: LXMF resource transfers through the V3 boundary node stall permanently at ~70% progress. The sender keeps retrying but never completes.
Bug 8b — T383838TcpInterface does not declare T383838FIXED_MTU
File: T383838TcpInterface.h, constructor
Code (before fix):
T282828
Te6edf3_HW_MTU Tff7b72= Te6edf3TCP_IF_HW_MTUTb4b4b4; T8b949e// 1064
T8b949e// _FIXED_MTU defaults to false
Impact: Even if Transport had MTU clamping code, it would skip clamping for this interface because T383838FIXED_MTU() returns T383838false. The interface's T383838HW_MTU value is not treated as authoritative.
Bug 8c — HDLC deframer silently truncates oversized frames
File: T383838TcpInterface.h, T383838_hdlc_deframe()
Code (before fix):
T282828
Tff7b72if Tb4b4b4(Te6edf3cTb4b4b4.Te6edf3rxlen Tff7b72< Te6edf3TCP_IF_HW_MTUTb4b4b4) Tb4b4b4{
Te6edf3cTb4b4b4.Te6edf3rxbufTb4b4b4[Te6edf3cTb4b4b4.Te6edf3rxlenTff7b72+Tff7b72+Tb4b4b4] Tff7b72= Te6edf3byteTb4b4b4;
Tb4b4b4}
T8b949e// Else: byte silently discarded, truncated frame delivered as if complete
Impact: When a client sends a frame larger than T383838TCP_IF_HW_MTU (1064 bytes), the deframer silently drops bytes beyond the buffer limit and delivers the truncated frame to Transport as if it were complete. This corrupts resource data segments and hashmap updates, causing the resource transfer protocol to stall.
Diagnosis
Serial log evidence (pre-fix):
• T383838LINK-XPORT: FWD entries show 5 T383838RESOURCE_DAT (ctx=1) segments forwarded, each silently truncated to 1064 bytes (from ~7500).
• After the 5th segment, no more T383838LINK-XPORT entries — the receiver's T383838RESOURCE_HMU (hashmap update, ctx=4) response is also truncated/corrupted and never processed.
• Receiver log: T383838"Timed out waiting for 4 parts, requesting retry" — retry also stalls.
Sender log evidence (pre-fix):
T282828
Signalling link MTU of 8.19 KB for link
Destination confirmed link MTU of 8.19 KB ← should have been clamped to 1064
The transfer of <LXMessage ...> is in progress (70.0%) ← stuck forever
Fix
8a — MTU clamping in T383838Transport.cpp (3 locations)
Added MTU clamping logic to all three T383838LINKREQUEST forwarding paths. When the path MTU in the link request exceeds T383838min(prev-hop HW_MTU, next-hop HW_MTU), the signalling bytes are rewritten using T383838Link::signalling_bytes(). If the outbound interface has no MTU or doesn't support MTU configuration, the signalling bytes are stripped entirely.
T282828
Tffa657uint16_t Te6edf3path_mtu Tff7b72= Te6edf3LinkTff7b72:Tff7b72:Te6edf3mtu_from_lr_packetTb4b4b4(Te6edf3packetTb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3path_mtu Tff7b72> T79c0ff0Tb4b4b4) Tb4b4b4{
Tffa657uint16_t Te6edf3ph_mtu Tff7b72= Te6edf3packetTb4b4b4.Te6edf3receiving_interfaceTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3HW_MTUTb4b4b4(Tb4b4b4)Tb4b4b4;
Tffa657uint16_t Te6edf3nh_mtu Tff7b72= Te6edf3outbound_interfaceTb4b4b4.Te6edf3HW_MTUTb4b4b4(Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3nh_mtu Tff7b72=Tff7b72= T79c0ff0Tb4b4b4) Tb4b4b4{
Te6edf3new_raw Tff7b72= Te6edf3new_rawTb4b4b4.Te6edf3leftTb4b4b4(Te6edf3new_rawTb4b4b4.Te6edf3sizeTb4b4b4(Tb4b4b4) Tff7b72- Te6edf3TypeTff7b72:Tff7b72:Te6edf3LinkTff7b72:Tff7b72:Te6edf3LINK_MTU_SIZETb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72else Tff7b72if Tb4b4b4(Tff7b72!Te6edf3outbound_interfaceTb4b4b4.Te6edf3AUTOCONFIGURE_MTUTb4b4b4(Tb4b4b4) Tff7b72&Tff7b72& Tff7b72!Te6edf3outbound_interfaceTb4b4b4.Te6edf3FIXED_MTUTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3new_raw Tff7b72= Te6edf3new_rawTb4b4b4.Te6edf3leftTb4b4b4(Te6edf3new_rawTb4b4b4.Te6edf3sizeTb4b4b4(Tb4b4b4) Tff7b72- Te6edf3TypeTff7b72:Tff7b72:Te6edf3LinkTff7b72:Tff7b72:Te6edf3LINK_MTU_SIZETb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72else Tff7b72if Tb4b4b4(Te6edf3nh_mtu Tff7b72< Te6edf3path_mtu Tff7b72|Tff7b72| Tb4b4b4(Te6edf3ph_mtu Tff7b72> T79c0ff0 Tff7b72&Tff7b72& Te6edf3ph_mtu Tff7b72< Te6edf3path_mtuTb4b4b4)Tb4b4b4) Tb4b4b4{
Tffa657uint16_t Te6edf3clamped Tff7b72= Te6edf3stdTff7b72:Tff7b72:Te6edf3minTb4b4b4(Te6edf3nh_mtuTb4b4b4, Tb4b4b4(Te6edf3ph_mtu Tff7b72> T79c0ff0Tb4b4b4) Tff7b72? Te6edf3ph_mtu Tff7b72: Te6edf3nh_mtuTb4b4b4)Tb4b4b4;
Tff7b72auto Te6edf3mode Tff7b72= Te6edf3LinkTff7b72:Tff7b72:Te6edf3mode_from_lr_packetTb4b4b4(Te6edf3packetTb4b4b4)Tb4b4b4;
Te6edf3Bytes Te6edf3clamped_mtu_bytes Tff7b72= Te6edf3LinkTff7b72:Tff7b72:Te6edf3signalling_bytesTb4b4b4(Te6edf3clampedTb4b4b4, Te6edf3modeTb4b4b4)Tb4b4b4;
Te6edf3new_raw Tff7b72= Te6edf3new_rawTb4b4b4.Te6edf3leftTb4b4b4(Te6edf3new_rawTb4b4b4.Te6edf3sizeTb4b4b4(Tb4b4b4) Tff7b72- Te6edf3TypeTff7b72:Tff7b72:Te6edf3LinkTff7b72:Tff7b72:Te6edf3LINK_MTU_SIZETb4b4b4) Tff7b72+ Te6edf3clamped_mtu_bytesTb4b4b4;
Tb4b4b4}
Tb4b4b4}
8b — T383838FIXED_MTU in T383838TcpInterface.h
Set T383838_FIXED_MTU = true in the constructor so Transport uses the interface's T383838HW_MTU (1064) for clamping decisions.
8c — Truncation detection in T383838TcpInterface.h
Added a T383838truncated flag to T383838TcpClient. Frames exceeding T383838TCP_IF_HW_MTU are now dropped with a diagnostic log instead of silently truncated:
T282828
[TcpIF] DROPPED oversized frame from client 1 (>1064 bytes, buffered 1064)
Verification
Sender log (post-fix):
T282828
Signalling link MTU of 8.19 KB for link
Destination confirmed link MTU of 1.06 KB ← clamped!
*** DELIVERY RESULT: DELIVERED (state=8) elapsed=2.8s ***
V3 serial log (post-fix):
T282828
MTU CLAMP: path=8192 ph=1064 nh=1064 -> clamped=1064
File integrity: SHA-256 of received T383838test.pdf matches original (T3838389bcb7b21d2bc7bbf...).
Test Reproduction
T282828
Tffa657cd test-harnesses/RTNode-HeltecV4
bash run_test.sh
Sends T383838test.pdf (46.1 KB) as an LXMF attachment through the V3 boundary node. Pre-fix: stalls at 70%. Post-fix: delivers in ~3 seconds.
Served by rngit 1.5.2 - Generated in 0.03s